package com.um.jdy.ykc.netty;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.um.jdy.common.base.enums.GunReturnStatus;
import com.um.jdy.common.base.enums.YesOrNo;
import com.um.jdy.common.device.enums.DeviceEnum;
import com.um.jdy.rpc.entity.EVDeviceInfo;
import com.um.jdy.rpc.enums.DeviceProtocolEnum;
import com.um.jdy.rpc.enums.DeviceTypeEnum;
import com.um.jdy.rpc.ev.confirmation.RemoteStartTransactionConfirmation;
import com.um.jdy.rpc.ev.confirmation.RemoteStopTransactionConfirmation;
import com.um.jdy.rpc.ev.confirmation.RemoteUpgradeConfirmation;
import com.um.jdy.rpc.ev.confirmation.StartChargeByRFIDConfirmation;
import com.um.jdy.rpc.ev.entity.*;
import com.um.jdy.rpc.ev.enums.ConfirmationStatus;
import com.um.jdy.rpc.ev.enums.StartType;
import com.um.jdy.rpc.ev.enums.StopType;
import com.um.jdy.rpc.ev.enums.SuspendType;
import com.um.jdy.rpc.notify.RpcEVBmsNotifyService;
import com.um.jdy.rpc.notify.RpcEVDeviceNotifyService;
import com.um.jdy.rpc.notify.RpcEVOrderNotifyService;
import com.um.jdy.ykc.callback.Callback;
import com.um.jdy.ykc.command.*;
import com.um.jdy.ykc.enums.CommandEnum;
import com.um.jdy.ykc.model.PriceModel;
import com.um.jdy.ykc.service.PriceModelService;
import com.um.jdy.ykc.utils.RadixsUtil;
import com.um.springcloud.dubbo.cluster.DeviceClusterUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 *
 * 业务处理类
 *
 * @author 邓南辉
 * @date 2020/3/5
 * @copyright 广州优匠科技有限公司
 */
@Slf4j
@Component
public class YkcCommandHandler{

    @Autowired
    PriceModelService priceModelService;

    @DubboReference
    RpcEVDeviceNotifyService rpcEVDeviceNotifyService;

    @DubboReference
    RpcEVOrderNotifyService rpcEVOrderNotifyService;

    @DubboReference
    RpcEVBmsNotifyService rpcEVBmsNotifyService;

    @Autowired
    CommandLogs commandLogs;

    @Autowired
    DeviceClusterUtil deviceClusterUtil;

    protected Map<String, Callback> mapCallback = new ConcurrentHashMap<>(16);
    protected Map<String, YkcCommand> mapResp = new ConcurrentHashMap<>(16);

    /**
     * 当客户端断开后，清除所有的回调方法
     *
     * @param id
     */
    @Async
    public void clearChannelCallback(String id) {
        Iterator<String> it = mapCallback.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            if (key.startsWith(id)) {
                mapCallback.remove(key);
            }
        }

        it = mapResp.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            if (key.startsWith(id)) {
                mapResp.remove(key);
            }
        }
    }

    /******************************业务代码*************************************/
    @Async
    public void handlerCommand(String code, YkcCommand command, ChannelHandlerContext context) {
        //记录command 上传日志，方便跟踪和调试
        commandLogs.addLogs(code, command);

        String key = getResponseKey(context, command);

        if(hasGetConfirmation(command))
            mapResp.put(key, command);

        if(mapCallback.containsKey(key)){
            Callback callback = mapCallback.get(key);
            callback.onCallback(command);
            mapCallback.remove(key);
            return;
        }

        int heartNum = NettyContextHolder.getHeartNum(context.channel().id().asLongText());

        if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd01LoginRequest.getKey())){//设备登录
            loginResponse(command,heartNum,context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd03HeartbeatRequest.getKey())){//心跳应答
            heartResponse(command,heartNum, context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd05VerifyPriceModelRequest.getKey())){//计费模型验证请求
            verifyPriceModelResponse(command,heartNum, context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd09GetPriceModelRequest.getKey())){//充电桩计费模型请求
            getPriceModelResponse(command,heartNum, context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd13UploadRealtimeData.getKey())){//上传实时监测数据
            uploadRealtimeData(command, context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd15ChargeHandshake.getKey())){//充电握手
            chargeHandshake(command, context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd17BmsSettings.getKey())){//参数配置
            bmsSettingsNotify(command, context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd19ChargeEnd.getKey())){//充电结束
            orderChargeStopNotify(command, context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd1BErrorReport.getKey())){//错误报文
            orderErrorNotify(command, context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd1DBmsStopWhenCharging.getKey())){//充电阶段 BMS 中止
            bmsStopNotify(command, context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd21ChargerStopWhenCharging.getKey())){//充电阶段充电机中止
            chargerStopNotify(command, context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd23BmsRequireWhenCharging.getKey())){//充电过程 BMS 需求、充电机输出
            chargingChargerNotify(command, context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd25BmsInfoWhenCharging.getKey())){//充电过程 BMS 信息
            chargingBmsNotify(command, context);
        }else if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd3BUploadOrderRequest.getKey())){//交易记录
            orderConfirm(command,heartNum,context);
        }else if(command.getCommand() ==  RadixsUtil.hexToInteger(CommandEnum.Cmd31DeviceStartChargeRequest.getKey())){//充电桩主动申请启动充电
            startRFIDCharge(command, context);
        }else if(command.getCommand() ==  RadixsUtil.hexToInteger(CommandEnum.Cmd33ServerStartChargeResponse.getKey())){//回复远程启动充电
            startCharge(command, context);
        }else if(command.getCommand() ==  RadixsUtil.hexToInteger(CommandEnum.Cmd35ServerStopChargeResponse.getKey())){//回复远程停止充电
            stopCharge(command, context);
        }else if(command.getCommand() ==  RadixsUtil.hexToInteger(CommandEnum.Cmd93UpgradeDeviceResponse.getKey())){//回复远程升级
            upgrade(command, context);
        }
    }

    /**
     * 是否需要记录结束
     * @param command
     * @return
     */
    private boolean hasGetConfirmation(YkcCommand command){
        if(command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd41UpdateBalanceResponse.getKey()) ||
                command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd55UpdateTimeResponse.getKey()) ||
                command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd91RebootDeviceResponse.getKey()) ||
                command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd51UpdateSettingsResponse.getKey()) ||
                command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd57UpdatePriceModelResponse.getKey()) ||
                command.getCommand() == RadixsUtil.hexToInteger(CommandEnum.Cmd55UpdateTimeResponse.getKey())){
            return true;
        }else{
            return false;
        }
    }

    /**
     * 设备离线通知
     *
     * @param imei
     */
    @Async
    public void deviceOfflineNotify(String imei) {
        if(StrUtil.isEmpty(imei)) return;

        rpcEVDeviceNotifyService.reqDeviceOffline("",imei);
    }

    /**
     * 登录认证应答
     * @param context
     */
    private void loginResponse(YkcCommand command,int heartNum,ChannelHandlerContext context){
        Cmd01LoginRequest req = (Cmd01LoginRequest)command;

        EVDeviceInfo deviceInfo = new EVDeviceInfo();
        deviceInfo.setHeart(60);
        deviceInfo.setIccid(req.getSimCard());
        deviceInfo.setVersion(req.getVersion());
        deviceInfo.setProtocol(DeviceProtocolEnum.YKC_16.getKey());
        deviceInfo.setType(DeviceTypeEnum.EV.name());
        String ipAddress = NetUtils.getLocalHost(); //当前中央服务实例IP
        deviceInfo.setIpAddress(ipAddress);
        boolean status = rpcEVDeviceNotifyService.reqDeviceLogin(req.getSerial()+"",req.getCode(),new ArrayList<>(),deviceInfo);

        Cmd02LoginResponse response = new Cmd02LoginResponse(command.getSerial(),req.getCode(),status ? 0 : 1);

        if(status){//登录后，绑定对应的socket字段
            deviceClusterUtil.setDeviceEndpoint(req.getCode(), NetUtils.getLocalHost());
            NettyContextHolder.putDeviceContext(req.getCode(), context);
            NettyChannelAttrs.setDeviceName(context, req.getCode());
            sendClient(response, context);

            //同步设备时间
            Cmd56UpdateTimeRequest request = new Cmd56UpdateTimeRequest(heartNum,req.getCode(),new Date());
            sendClient(request, context);
            log.info("设备[{}]登录成功：{} {}", req.getCode(), context.channel().remoteAddress(), context.channel().id());
        }else{
            log.info("设备[{}]登录失败：{} {}", req.getCode(), context.channel().remoteAddress(), context.channel().id());
            context.channel().close();//断开
        }
    }

    /**
     * 心跳包应答
     * @param context
     */
    private void heartResponse(YkcCommand command,int heartNum,ChannelHandlerContext context){
        Cmd03HeartbeatRequest req = (Cmd03HeartbeatRequest)command;

        if(null == NettyContextHolder.getDeviceContext(req.getCode())){
            log.warn("设备[{}]心跳重新绑定上下文：{} {}", req.getCode(), context.channel().remoteAddress(), context.channel().id());
            deviceClusterUtil.setDeviceEndpoint(req.getCode(), NetUtils.getLocalHost());
            NettyContextHolder.putDeviceContext(req.getCode(), context);
            NettyChannelAttrs.setDeviceName(context, req.getCode());
        }

        rpcEVDeviceNotifyService.reqDeviceHeart(req.getSerial()+"",req.getCode(),DeviceProtocolEnum.YKC_16.getKey());

        Cmd04HeartbeatResponse response = new Cmd04HeartbeatResponse(command.getSerial(),req.getCode(),req.getGunNo(),0);
        sendClient(response,context);
    }

    /**
     * 计费模型验证请求应答
     * @param context
     */
    private void verifyPriceModelResponse(YkcCommand command,int heartNum,ChannelHandlerContext context){
        Cmd05VerifyPriceModelRequest req = (Cmd05VerifyPriceModelRequest)command;

        //固定返回失败，每次登录都要重新下发计费模型
        Cmd06VerifyPriceModelResponse response = new Cmd06VerifyPriceModelResponse(command.getSerial(),req.getCode(),"0100",1);
        sendClient(response,context);
    }

    /**
     * 计费模型请求应答
     * @param context
     */
    private void getPriceModelResponse(YkcCommand command,int heartNum,ChannelHandlerContext context){
        Cmd09GetPriceModelRequest req = (Cmd09GetPriceModelRequest)command;
        PriceModel model = priceModelService.getPriceModel(req.getCode());

        if(model != null) {
            Cmd0AGetPriceModelResponse response = new Cmd0AGetPriceModelResponse(command.getSerial(), req.getCode(), model.getFeeMode(), model.getSharpFee(),
                    model.getSharpServiceFee(), model.getPeakFee(), model.getPeakServiceFee(), model.getShoulderFee(), model.getShoulderServiceFee(),
                    model.getOffPeakFee(), model.getOffPeakServiceFee(), model.getLossScale(), model.getTime48Fee());
            sendClient(response, context);
        }
    }

    /**
     * 上传实时监测数据
     * @param context
     */
    private void uploadRealtimeData(YkcCommand command,ChannelHandlerContext context){
        Cmd13UploadRealtimeData req = (Cmd13UploadRealtimeData)command;

        EVRealtimeData data = new EVRealtimeData();
        BeanUtils.copyProperties(req,data);
        switch(req.getStatus()){
            case 0:
                data.setStatus(DeviceEnum.PortStatus.Offline.name());
                break;
            case 1:
                data.setStatus(DeviceEnum.PortStatus.Faulted.name());
                break;
            case 2:
                data.setStatus(DeviceEnum.PortStatus.Available.name());
                break;
            case 3:
                data.setStatus(DeviceEnum.PortStatus.Charging.name());
                break;
        }

        if(req.getGunInserted() == 1 && req.getStatus() == 2){
            data.setStatus(DeviceEnum.PortStatus.Preparing.name());
        }

        data.setGunInserted(req.getGunInserted() == 0 ? YesOrNo.No.name() : YesOrNo.Yes.name());
        switch (req.getGunReturned()){
            case 0:
                data.setGunReturned(GunReturnStatus.No.name());
                break;
            case 1:
                data.setGunReturned(GunReturnStatus.Yes.name());
                break;
            case 2:
                data.setGunReturned(GunReturnStatus.Unknown.name());
                break;
        }

        byte[] errors = RadixsUtil.bytesToBits(RadixsUtil.int16ToBytes(req.getHardwareError()));
        for(int index = 0;index < errors.length;index ++){
            if(errors[index] != 0){
                data.getHardwareError().add("YKC_"+RadixsUtil.byteToInteger(errors[index]));
            }
        }
        rpcEVOrderNotifyService.reqRealtimeData(req.getSerial()+"",req.getCode(),data);
    }


    /**
     * 充电握手通知
     * @param context
     */
    private void chargeHandshake(YkcCommand command,ChannelHandlerContext context){
        Cmd15ChargeHandshake req = (Cmd15ChargeHandshake)command;

        BmsInfoData data = new BmsInfoData();
        BeanUtils.copyProperties(req,data);
        rpcEVBmsNotifyService.reqBmsInfoNofity(req.getCode(),data);
    }

    /**
     * 参数配置
     * @param context
     */
    private void bmsSettingsNotify(YkcCommand command,ChannelHandlerContext context){
        Cmd17BmsSettings req = (Cmd17BmsSettings)command;

        BmsSettingsData data = new BmsSettingsData();
        BeanUtils.copyProperties(req,data);
        rpcEVBmsNotifyService.reqBmsSettingsNofity(req.getCode(),data);
    }

    /**
     * 充电结束 cmd_19
     *
     */
    private void orderChargeStopNotify(YkcCommand command,ChannelHandlerContext context){
        Cmd19ChargeEnd req = (Cmd19ChargeEnd)command;

        EVChargingEndData data = new EVChargingEndData();
        BeanUtils.copyProperties(req,data);
        rpcEVOrderNotifyService.reqChargingEnd(req.getSerial()+"",req.getCode(),data);
    }

    /**
     * cmd_1B
     * 错误报文主动上报
     */
    private void orderErrorNotify(YkcCommand command,ChannelHandlerContext context){
        Cmd1BErrorReport req = (Cmd1BErrorReport)command;

        EVChargingErrorData data = new EVChargingErrorData();
        BeanUtils.copyProperties(req,data);
        rpcEVOrderNotifyService.reqChargingError(req.getSerial()+"",req.getCode(),data);
    }

    /**
     * cmd_1D
     * 电池中止充电主动上报：充电订单应显示暂停充电
     */
    private void bmsStopNotify(YkcCommand command,ChannelHandlerContext context){
        Cmd1DBmsStopWhenCharging req = (Cmd1DBmsStopWhenCharging)command;

        EVSuspendData data = new EVSuspendData();
        data.setSuspendType(SuspendType.Bms);
        //TODO 设置不同的原因
        rpcEVOrderNotifyService.reqChargingSuspend(req.getSerial()+"",req.getCode(),data);
    }

    /**
     * cmd_21
     * 充电机中止主动上报：充电订单应显示暂停充电
     */
    private void chargerStopNotify(YkcCommand command,ChannelHandlerContext context){
        Cmd21ChargerStopWhenCharging req = (Cmd21ChargerStopWhenCharging)command;

        EVSuspendData data = new EVSuspendData();
        data.setSuspendType(SuspendType.Charger);
        //TODO 设置不同的原因
        rpcEVOrderNotifyService.reqChargingSuspend(req.getSerial()+"",req.getCode(),data);
    }

    /**
     * cmd_23
     * 充电过程 充电桩信息上报
     */
    private void chargingChargerNotify(YkcCommand command,ChannelHandlerContext context){
        Cmd23BmsRequireWhenCharging req = (Cmd23BmsRequireWhenCharging)command;

        EVChargingChargerData data = new EVChargingChargerData();
        BeanUtils.copyProperties(req,data);
        rpcEVOrderNotifyService.reqChargingChargerInfo(req.getSerial()+"",req.getCode(),data);
    }

    /**
     * cmd_25
     * 充电过程 BMS信息主动上报
     */
    private void chargingBmsNotify(YkcCommand command,ChannelHandlerContext context){
        Cmd25BmsInfoWhenCharging req = (Cmd25BmsInfoWhenCharging)command;

        EVChargingBmsData data = new EVChargingBmsData();
        BeanUtils.copyProperties(req,data);
        rpcEVOrderNotifyService.reqChargingBmsInfo(req.getSerial()+"",req.getCode(),data);
    }

    /**
     * cmd_3B
     * 交易记录上报
     * 交易数据
     */
    private void orderConfirm(YkcCommand command,int heartNum,ChannelHandlerContext context){
        Cmd3BUploadOrderRequest req = (Cmd3BUploadOrderRequest)command;

        EVOrderData data = new EVOrderData();
        BeanUtils.copyProperties(req,data);
        StartType startType = EnumUtil.getBy(StartType::getCode, req.getStartType());
        data.setStartType(startType);

        StopType stopType = EnumUtil.getBy(StopType::getCode, req.getEndType());
        if(null == stopType){
            stopType = StopType.UnknownReason;
        }
        data.setEndType(stopType);

        boolean status = rpcEVOrderNotifyService.reqConfirmOrder(heartNum+"",req.getCode(),data);

        Cmd40UploadOrderResponse response = new Cmd40UploadOrderResponse(command.getSerial(),req.getTransactionNo(),status ? 0 : 1);
        sendClient(response,context);
    }

    /**
     * 刷卡功能暂时不使用
     *
     * cmd_32
     * 运营平台确认启动充电
     * @param command
     * @param context
     */
    private void startRFIDCharge(YkcCommand command,ChannelHandlerContext context){
        Cmd31DeviceStartChargeRequest req = (Cmd31DeviceStartChargeRequest)command;


        EVStartChargeData startChargeData = BeanUtil.copyProperties(req, EVStartChargeData.class);
        StartChargeByRFIDConfirmation confirmation = rpcEVOrderNotifyService.reqStartCharge(String.valueOf(req.getSerial()),
                req.getCode(), startChargeData);
        log.info("设备主动请求开启充电结果:\n{}", JSONUtil.toJsonStr(confirmation));

        if(null == confirmation || !confirmation.isAuthentication()){
            return;
        }

        Integer balance = confirmation.getBalance();
        if(balance >= 0){
            balance = balance / 100;
        }
        Cmd32DeviceStartChargeResponse response = new Cmd32DeviceStartChargeResponse(command.getSerial(),
                confirmation.getTransactionNo() == null ? "0000000000000000" : confirmation.getTransactionNo(),
                req.getCode(), req.getGunNo(), req.getCardNo(),
                balance, confirmation.isAuthentication(), confirmation.getFaultReason());
        sendClient(response,context);

    }

    /**
     * cmd 33
     * 回复远程开启充电
     * @param command
     * @param context
     */
    private void startCharge(YkcCommand command,ChannelHandlerContext context){
        Cmd33ServerStartChargeResponse req = (Cmd33ServerStartChargeResponse)command;
        RemoteStartTransactionConfirmation confirmation = new RemoteStartTransactionConfirmation();
        if(req.getStatus() == 1){
            confirmation.setStatus(ConfirmationStatus.Accepted);
        }else{
            confirmation.setStatus(ConfirmationStatus.Rejected);
            switch (req.getFaultReason()){
                case 0:
                    confirmation.setReason("未知原因");
                    break;
                case 1:
                    confirmation.setReason("设备编号不匹配");
                    break;
                case 2:
                    confirmation.setReason("枪已在充电");
                    break;
                case 3:
                    confirmation.setReason("设备故障");
                    break;
                case 4:
                    confirmation.setReason("设备离线");
                    break;
                case 5:
                    confirmation.setReason("未插枪");
                    break;
            }
        }

        rpcEVOrderNotifyService.respStartCharged(req.getSerial()+"",req.getCode(),req.getTransactionNo(),req.getGunNo(),confirmation);

    }

    /**
     * cmd 35
     * 回复远程停止充电
     * @param command
     * @param context
     */
    private void stopCharge(YkcCommand command,ChannelHandlerContext context){
        Cmd35ServerStopChargeResponse req = (Cmd35ServerStopChargeResponse)command;
        RemoteStopTransactionConfirmation confirmation = new RemoteStopTransactionConfirmation();
        if (req.getStatus() == 1) {
            confirmation.setStatus(ConfirmationStatus.Accepted);
        } else {
            confirmation.setStatus(ConfirmationStatus.Rejected);
            switch (req.getFaultReason()){
                case 0:
                    confirmation.setReason("未知原因");
                    break;
                case 1:
                    confirmation.setReason("设备编号不匹配");
                    break;
                case 2:
                    confirmation.setReason("枪未处于充电状态");
                    break;
                case 3:
                    confirmation.setReason("其他");
                    break;
            }
        }

        rpcEVOrderNotifyService.respStopCharged(req.getSerial()+"",req.getCode(),req.getGunNo(),confirmation);
    }

    /**
     * cmd 93
     * 回复远程升级
     * @param command
     * @param context
     */
    private void upgrade(YkcCommand command,ChannelHandlerContext context){
        Cmd93UpgradeDeviceResponse req = (Cmd93UpgradeDeviceResponse)command;
        RemoteUpgradeConfirmation confirmation = new RemoteUpgradeConfirmation();
        if (req.getStatus() == 0) {
            confirmation.setStatus(ConfirmationStatus.Accepted);
        } else {
            confirmation.setStatus(ConfirmationStatus.Rejected);
            if(req.getStatus() == 1){
                confirmation.setReason("编号错误");
            }else if(req.getStatus() == 2){
                confirmation.setReason("程序与桩型号不符");
            }else{
                confirmation.setReason("下载更新文件超时");
            }
        }

        rpcEVDeviceNotifyService.respUpgrade(req.getSerial()+"",req.getCode(),confirmation);

    }


    /**
     * 发送数据回客户端
     * @param command 数据
     * @param context
     */
    public void sendClient(YkcCommand command, ChannelHandlerContext context, Callback callback) {
        try {
            //保存回调的方法
            if(callback != null) {
                String key = getRequestKey(context, command);
                mapCallback.put(key,callback);
            }

            byte[] bytes = command.toBytes();
            log.debug("发送数据：{}", RadixsUtil.bytesToHexString(bytes));

            ByteBuf buff = Unpooled.buffer();//netty需要用ByteBuf传输
            buff.writeBytes(bytes);//对接需要16进制
            context.writeAndFlush(buff).addListener(future -> log.debug("发送命令{}：{}", future.isSuccess() ? "成功" : "失败", command));

            String code = NettyChannelAttrs.getDeviceName(context);
            commandLogs.addLogs(code, command);
        } catch (Exception e) {
            log.error("sendClient异常", e);
        }
    }

    /**
     * 发送数据回客户端
     * @param command 数据
     * @param context
     */
    public void sendClient(YkcCommand command, ChannelHandlerContext context) {
        sendClient(command, context,null);
    }

    /**
     * 获取缓存的接收命令
     * @param key
     * @return
     */
    public <T extends YkcCommand> T getResp(String key){
        log.info(JSONUtil.toJsonStr(mapResp));
        T resp = (T) mapResp.get(key);
        if(null != resp){
            mapResp.remove(key);
        }
        return resp;
    }

    /**
     * 生成发送时缓存的key
     * @param context
     * @param command
     * @return
     */
    private String getRequestKey(ChannelHandlerContext context, YkcCommand command){
        String key = context.channel().id().asLongText()+"_"+command.getCommand()+"_"+command.getSerial();
        return key;
    }

    /**
     * 生成充电桩回复时的Key,这里需要把回复的cmd转换为发送时的cmd
     * @param context
     * @param command
     * @return
     */
    private String getResponseKey(ChannelHandlerContext context, YkcCommand command){
        //command.getCommand() + 1 等于发送的命令
        String key = context.channel().id().asLongText()+"_"+(command.getCommand() + 1)+"_"+command.getSerial();
        return key;
    }
}
